package com.share.common.security.service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.share.common.core.constant.CacheConstants;
import com.share.common.core.constant.SecurityConstants;
import com.share.common.core.utils.JwtUtils;
import com.share.common.core.utils.ServletUtils;
import com.share.common.core.utils.StringUtils;
import com.share.common.core.utils.ip.IpUtils;
import com.share.common.core.utils.uuid.IdUtils;
import com.share.common.redis.service.RedisService;
import com.share.common.security.utils.SecurityUtils;
import com.share.system.api.model.LoginUser;
import org.springframework.stereotype.Service;

/**
 * 负责 Token 的创建、验证和管理
 *
 */
@Slf4j
@Service
public class TokenService {

    @Autowired
    private RedisService redisService;

    // 单位 1s
    protected static final long MILLIS_SECOND = 1000;
    // 单位 1min
    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
    //过期时间 720min
    private final static long expireTime = CacheConstants.EXPIRATION;
    //redis 权限相关key前缀名
    private final static String ACCESS_TOKEN = CacheConstants.LOGIN_TOKEN_KEY;
    //redis 缓存数据刷新时间
    private final static Long MILLIS_MINUTE_TEN = CacheConstants.REFRESH_TIME * MILLIS_MINUTE;

    /**
     * 创建token令牌 并存入redis
     */
    public Map<String, Object> createToken(LoginUser loginUser) {
        //如果直接使用用户 ID 或其他固定值作为 userKey，可能会在多个用户之间产生冲突，
        // 尤其是在分布式系统中。UUID 的唯一性可以有效避免这种问题。
        //随机的uuid作为redis中用户的唯一标识 是一个全局唯一的标识符，生成的字符串几乎不会重复。
        // 使用 UUID 作为 userKey 可以确保每个用户的 Token 都是唯一的，避免冲突。
        String uuid = IdUtils.fastUUID();

        //登录用户id 用户名
        Long userId = loginUser.getUserid();
        String userName = loginUser.getUsername();

        loginUser.setToken(uuid);
        loginUser.setUserid(userId);
        loginUser.setUsername(userName);
        loginUser.setIpaddr(IpUtils.getIpAddr()); //设置登录用户的ip

        //将用户信息存入redis
        refreshToken(loginUser);

        // token中要存储的数据
        Map<String, Object> dataMap = new HashMap<String, Object>();
        dataMap.put(SecurityConstants.USER_KEY, uuid); // uuid，用户唯一标识
        dataMap.put(SecurityConstants.DETAILS_USER_ID, userId); //用户id
        dataMap.put(SecurityConstants.DETAILS_USERNAME, userName); //用户名


        Map<String, Object> rspMap = new HashMap<String, Object>();

        // 生成token，并将存储的数据放入token中
        String token = JwtUtils.createToken(dataMap);
        rspMap.put("access_token", token);
        rspMap.put("expires_in", expireTime); // 设置token过期时间 默认为3小时
        return rspMap;
    }

    /**
     * 获取用户身份信息 前端请求后，获取token
     *
     * @return 用户信息
     */
    public LoginUser getLoginUser() {
        return getLoginUser(ServletUtils.getRequest());
    }

    /**
     * 获取前端请求的携带的token
     *
     * @return 用户信息
     */
    public LoginUser getLoginUser(HttpServletRequest request) {
        // 获取请求携带的令牌
        String token = SecurityUtils.getToken(request);
        return getLoginUser(token);
    }

    /**
     * 从前端请求中获取 Token，并从 Redis 中查询对应的用户信息。
     *
     * @return 用户信息
     */
    public LoginUser getLoginUser(String token) {
        LoginUser user = null;
        try
        {
            if (StringUtils.isNotEmpty(token))
            {
                String userkey = JwtUtils.getUserKey(token);
                user = redisService.getCacheObject(getTokenKey(userkey));
                return user;
            }
        }
        catch (Exception e)
        {
            log.error("获取用户信息异常'{}'", e.getMessage());
        }
        return user;
    }

    /**
     * 设置用户身份信息
     */
    public void setLoginUser(LoginUser loginUser) {
        if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken()))
        {
            refreshToken(loginUser);
        }
    }

    /**
     * 删除用户缓存信息
     */
    public void delLoginUser(String token) {
        if (StringUtils.isNotEmpty(token))
        {
            String userkey = JwtUtils.getUserKey(token);
            redisService.deleteObject(getTokenKey(userkey));
        }
    }

    /**
     * 验证令牌有效期，相差不足120分钟，自动刷新缓存
     *
     * @param loginUser
     */
    public void verifyToken(LoginUser loginUser) {
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
        {
            refreshToken(loginUser);
        }
    }

    /**
     * 刷新令牌有效期
     *
     * @param loginUser 登录信息
     */
    public void refreshToken(LoginUser loginUser) {
        loginUser.setLoginTime(System.currentTimeMillis()); //登录时间
        loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); //token过期时间,登录时间加有效时间,之后需要重新登陆

        // 将登录用户的token存入redis缓存 并设置过期时间和单位
        String userKey = getTokenKey(loginUser.getToken());
        redisService.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
    }

    private String getTokenKey(String token) {
        return ACCESS_TOKEN + token;
    }
}
